package com.boilermaker.lotterysystem.service.mq;

import com.boilermaker.lotterysystem.common.exception.ServiceException;
import com.boilermaker.lotterysystem.common.utils.JacksonUtil;
import com.boilermaker.lotterysystem.common.utils.MailUtil;
import com.boilermaker.lotterysystem.controller.param.DrawPrizeParam;
import com.boilermaker.lotterysystem.dao.dataobject.WinningRecordDO;
import com.boilermaker.lotterysystem.service.DrawPrizeService;
import com.boilermaker.lotterysystem.service.async.AsyncTaskExecutorService;
import com.boilermaker.lotterysystem.service.async.asynctask.EmailNotificationTask;
import com.boilermaker.lotterysystem.service.dto.ConvertStatusDTO;
import com.boilermaker.lotterysystem.service.dto.EmailNotificationTaskDTO;
import com.boilermaker.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.boilermaker.lotterysystem.service.enums.ActivityStatusEnum;
import com.boilermaker.lotterysystem.service.enums.ActivityUserStatusEnum;
import com.boilermaker.lotterysystem.service.statusconvert.StatusConvertManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.boilermaker.lotterysystem.common.config.LotteryRabbitMQConfig.LOTTERY_QUEUE;

@Component
@RabbitListener(queues = LOTTERY_QUEUE)
@Slf4j
public class LotteryRabbitMQReceiver {

    @Autowired
    private DrawPrizeService drawPrizeService;

    @Autowired
    private StatusConvertManager statusConvertManager;

    @Autowired
    private AsyncTaskExecutorService asyncTaskExecutorService;

    @Autowired
    private TransactionTemplate transactionTemplate; // 使用编程式事务

    /**
     * 消费消息主流程
     */
    @RabbitHandler
    public void process(Map<String, String> message) {
        log.info("Broker 消费消息：messageId = {}",
                JacksonUtil.writeValueAsString(message.get("messageId")));

        String messageBody = message.get("messageBody");
        DrawPrizeParam param = JacksonUtil.readValue(messageBody, DrawPrizeParam.class);

        try {
            // 校验参数有效性，保证幂等
            drawPrizeService.checkDrawPrizeParam(param);

            // 事务
            List<WinningRecordDO> prizeWinnerRecords = transactionTemplate.execute(action -> {
                statusConvert(param); // 状态扭转
                return drawPrizeService.saveWinningRecords(param); // 保存中奖记录
            });

            // 抽奖后续业务流程，均进行异步处理（内部捕获异常）
            asyncMailNotification(prizeWinnerRecords);
            // ...

        } catch (ServiceException e) {
            log.warn("Broker 处理消息发生业务异常，{} : {}", e.getCode(), e.getMsg(), e);
        } catch (Exception e) {
            log.error("Broker 处理消息发生系统异常，尝试重发消息", e);
            throw e; // 使 RabbitMQ 感知异常以进行重发
        }
    }

    /**
     * 中奖邮件通知（内部已捕获异常，不会抛出导致事务回滚）
     */
    private void asyncMailNotification(List<WinningRecordDO> prizeWinnerRecords) {

        if (CollectionUtils.isEmpty(prizeWinnerRecords)) {
            log.warn("向中奖者发送邮件失败，因为参数为空");
            return;
        }

        // Param -> DTO
        List<EmailNotificationTaskDTO> dtoList = prizeWinnerRecords.stream()
                .map(winnerRecord -> {
                    EmailNotificationTaskDTO dto = new EmailNotificationTaskDTO();

                    dto.setWinnerEmail(winnerRecord.getWinnerEmail());
                    dto.setWinnerName(winnerRecord.getWinnerName());
                    dto.setActivityName(winnerRecord.getActivityName());
                    dto.setPrizeName(winnerRecord.getPrizeName());
                    dto.setPrizeTier(winnerRecord.getPrizeTier());
                    dto.setWinningTime(winnerRecord.getWinningTime());

                    return dto;
                }).toList();

        for (EmailNotificationTaskDTO dto : dtoList) {
            asyncTaskExecutorService.submitTask("emailNotificationTask", dto)
                    .thenRun(() -> log.info("向 {} 发送中奖通知邮件成功", dto.getWinnerName()))
                    .exceptionally(e -> {
                        log.error("向中奖者发送邮件失败", e);
                        return null;
                    });
        }
    }

    /**
     * 状态扭转
     */
    private void statusConvert(DrawPrizeParam param) {

        // Param -> DTO
        ConvertStatusDTO dto = new ConvertStatusDTO();
        dto.setActivityId(param.getActivityId());
        dto.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        dto.setPrizeId(param.getPrizeId());
        dto.setTargetPrizeStatus(ActivityPrizeStatusEnum.UNAVAILABLE);
        dto.setUserIds(
                param.getWinnerList()
                        .stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        dto.setTargetUserStatus(ActivityUserStatusEnum.UNAVAILABLE);

        // 调用状态扭转模块
        statusConvertManager.manageStatusConvert(dto);
    }
}
